package com.github.andreptb.fitnesse.selenium;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.github.andreptb.fitnesse.util.FitnesseMarkup;
public class SeleniumLocatorParser {
/**
* Utility to process FitNesse markup
*/
private FitnesseMarkup fitnesseMarkup = new FitnesseMarkup();
/**
* enum mapping selector identifier with selector implementation ({@link By} implementations).
*/
private enum LocatorType {
id(By.ById.class),
name(By.ByName.class),
css(By.ByCssSelector.class),
xpath(By.ByXPath.class),
link(By.ByLinkText.class);
private Class<? extends By> byClass;
LocatorType(Class<? extends By> byClass) {
this.byClass = byClass;
}
}
/**
* Parses locator to an instance of {@link WebElementSelector}. Tries to emulate Selenium IDE searching methods:
* <ul>
* <li>By id: 'id=<id>'</li>
* <li>By name: 'name=<name>'</li>
* <li>By css selector: 'css=#<id>'</li>
* <li>By link text selector: 'link=#<linktext>'</li>
* <li>By xpath selector: 'div[@id=<id>]'</li>
* </ul>
* Just like SeleniumIDE, attribute name is parsed from selector when element locator is followed by an @ sign and then the name of the attribute. For Example:
* <ul>
* <li>'id=<id>@<attributeName>'</li>
* </ul>
*
* @param locator to be parsed
* @return instance of {@link WebElementSelector}
*/
public WebElementSelector parse(String locator) {
Pair<String, String> elementAndValue = this.fitnesseMarkup.cleanAndParseKeyValue(locator, FitnesseMarkup.SELECTOR_VALUE_SEPARATOR);
return new WebElementSelector(elementAndValue.getKey(), parseBy(elementAndValue.getKey()), elementAndValue.getValue());
}
private By parseBy(String locator) {
if (StringUtils.isBlank(locator)) {
return new ByFocus();
}
Pair<String, String> prefixAndSelector = this.fitnesseMarkup.cleanAndParseKeyValue(locator, FitnesseMarkup.KEY_VALUE_SEPARATOR);
String prefix = prefixAndSelector.getKey();
String selector = prefixAndSelector.getValue();
LocatorType selectorType = EnumUtils.getEnum(LocatorType.class, prefix);
if (selectorType == null) {
selector = locator;
selectorType = LocatorType.xpath;
}
try {
return selectorType.byClass.getConstructor(String.class).newInstance(selector);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Unexpected failure instantiating selector: " + prefix, e);
}
}
/**
* {@link By} implementation that returns the currently focused element
*/
public static class ByFocus extends By {
@Override
public List<WebElement> findElements(SearchContext context) {
if (context instanceof WebDriver) {
return Arrays.asList(((WebDriver) context).switchTo().activeElement());
}
return null;
}
}
public static class WebElementSelector {
private String originalSelector;
private By by;
private String expectedValue;
public WebElementSelector(String originalSelector, By by, String expectedValue) {
this.originalSelector = originalSelector;
this.by = by;
this.expectedValue = expectedValue;
}
public String getOriginalSelector() {
return this.originalSelector;
}
public By getBy() {
return this.by;
}
public String getExpectedValue() {
return this.expectedValue;
}
}
}